package eva

import (
	"fmt"
	"testing"

	"gitcode.com/deyiyangyang/gascheme/ast"
	"gitcode.com/deyiyangyang/gascheme/lex"
	"gitcode.com/deyiyangyang/gascheme/obj"
	"gitcode.com/deyiyangyang/gascheme/par"
	"gitcode.com/deyiyangyang/gascheme/tok"
)

// TestIntegerEvaluation tests the evaluation of integer literals
func TestIntegerEvaluation(t *testing.T) {
	// Create an integer literal AST node
	intNode := &ast.IntegerLiteral{
		Token: tok.Token{Type: tok.INT, Lexeme: "42"},
		Value: 42,
	}

	// Create an environment
	env := obj.NewGlobalEnvironment()

	// Evaluate the node
	result := Eval(intNode, env)

	// Check if the result is an Integer object with the correct value
	integer, ok := result.(*obj.Integer)
	if !ok {
		t.Errorf("Eval didn't return Integer. Got %T", result)
		return
	}

	if integer.Value != 42 {
		t.Errorf("Eval returned wrong value. Got %d, want %d", integer.Value, 42)
	}
}

// TestRationalEvaluation tests the evaluation of rational literals
func TestRationalEvaluation(t *testing.T) {
	// Create a rational literal AST node
	rationalNode := &ast.RationalLiteral{
		Token:       tok.Token{Type: tok.RATIONAL, Lexeme: "3/2"},
		Numerator:   3,
		Denominator: 2,
	}

	// Create an environment
	env := obj.NewGlobalEnvironment()

	// Evaluate the node
	result := Eval(rationalNode, env)

	// Check if the result is a Rational object with the correct values
	rational, ok := result.(*obj.Rational)
	if !ok {
		t.Errorf("Eval didn't return Rational. Got %T", result)
		return
	}

	if rational.Numerator != 3 || rational.Denominator != 2 {
		t.Errorf("Eval returned wrong value. Got %d/%d, want 3/2", rational.Numerator, rational.Denominator)
	}
}

// TestRealEvaluation tests the evaluation of real literals
func TestRealEvaluation(t *testing.T) {
	// Create a real literal AST node
	realNode := &ast.RealLiteral{
		Token: tok.Token{Type: tok.REAL, Lexeme: "3.14"},
		Value: 3.14,
	}

	// Create an environment
	env := obj.NewGlobalEnvironment()

	// Evaluate the node
	result := Eval(realNode, env)

	// Check if the result is a Real object with the correct value
	real, ok := result.(*obj.Real)
	if !ok {
		t.Errorf("Eval didn't return Real. Got %T", result)
		return
	}

	if real.Value != 3.14 {
		t.Errorf("Eval returned wrong value. Got %f, want %f", real.Value, 3.14)
	}
}

// TestIntegerAddition tests the evaluation of integer addition expressions
func TestIntegerAddition(t *testing.T) {
	// Create a list expression for (+ 1 2 3)
	addExpr := &ast.ListExpression{
		Token: tok.Token{Type: tok.LPAREN, Lexeme: "("},
		Elements: []ast.Expression{
			&ast.SymbolLiteral{
				Token: tok.Token{Type: tok.SYMBOL, Lexeme: "+"},
				Value: "+",
			},
			&ast.IntegerLiteral{
				Token: tok.Token{Type: tok.INT, Lexeme: "1"},
				Value: 1,
			},
			&ast.IntegerLiteral{
				Token: tok.Token{Type: tok.INT, Lexeme: "2"},
				Value: 2,
			},
			&ast.IntegerLiteral{
				Token: tok.Token{Type: tok.INT, Lexeme: "3"},
				Value: 3,
			},
		},
	}

	// Create an environment and register built-in procedures
	env := obj.NewGlobalEnvironment()
	RegisterBuiltinProcedures(env)

	// Evaluate the expression
	result := Eval(addExpr, env)

	// Check if the result is an Integer object with the correct value
	integer, ok := result.(*obj.Integer)
	if !ok {
		t.Errorf("Eval didn't return Integer. Got %T", result)
		return
	}

	if integer.Value != 6 {
		t.Errorf("Eval returned wrong value for (+ 1 2 3). Got %d, want 6", integer.Value)
	}
}

// TestIntegerSubtraction tests the evaluation of integer subtraction expressions
func TestIntegerSubtraction(t *testing.T) {
	// Create a list expression for (- 10 5 2)
	subExpr := &ast.ListExpression{
		Token: tok.Token{Type: tok.LPAREN, Lexeme: "("},
		Elements: []ast.Expression{
			&ast.SymbolLiteral{
				Token: tok.Token{Type: tok.SYMBOL, Lexeme: "-"},
				Value: "-",
			},
			&ast.IntegerLiteral{
				Token: tok.Token{Type: tok.INT, Lexeme: "10"},
				Value: 10,
			},
			&ast.IntegerLiteral{
				Token: tok.Token{Type: tok.INT, Lexeme: "5"},
				Value: 5,
			},
			&ast.IntegerLiteral{
				Token: tok.Token{Type: tok.INT, Lexeme: "2"},
				Value: 2,
			},
		},
	}

	// Create an environment and register built-in procedures
	env := obj.NewGlobalEnvironment()
	RegisterBuiltinProcedures(env)

	// Evaluate the expression
	result := Eval(subExpr, env)

	// Check if the result is an Integer object with the correct value
	integer, ok := result.(*obj.Integer)
	if !ok {
		t.Errorf("Eval didn't return Integer. Got %T", result)
		return
	}

	if integer.Value != 3 {
		t.Errorf("Eval returned wrong value for (- 10 5 2). Got %d, want 3", integer.Value)
	}
}

// TestIntegerMultiplication tests the evaluation of integer multiplication expressions
func TestIntegerMultiplication(t *testing.T) {
	// Create a list expression for (* 2 3 4)
	mulExpr := &ast.ListExpression{
		Token: tok.Token{Type: tok.LPAREN, Lexeme: "("},
		Elements: []ast.Expression{
			&ast.SymbolLiteral{
				Token: tok.Token{Type: tok.SYMBOL, Lexeme: "*"},
				Value: "*",
			},
			&ast.IntegerLiteral{
				Token: tok.Token{Type: tok.INT, Lexeme: "2"},
				Value: 2,
			},
			&ast.IntegerLiteral{
				Token: tok.Token{Type: tok.INT, Lexeme: "3"},
				Value: 3,
			},
			&ast.IntegerLiteral{
				Token: tok.Token{Type: tok.INT, Lexeme: "4"},
				Value: 4,
			},
		},
	}

	// Create an environment and register built-in procedures
	env := obj.NewGlobalEnvironment()
	RegisterBuiltinProcedures(env)

	// Evaluate the expression
	result := Eval(mulExpr, env)

	// Check if the result is an Integer object with the correct value
	integer, ok := result.(*obj.Integer)
	if !ok {
		t.Errorf("Eval didn't return Integer. Got %T", result)
		return
	}

	if integer.Value != 24 {
		t.Errorf("Eval returned wrong value for (* 2 3 4). Got %d, want 24", integer.Value)
	}
}

// TestIntegerDivision tests the evaluation of integer division expressions
func TestIntegerDivision(t *testing.T) {
	// Create a list expression for (/ 12 3 2)
	divExpr := &ast.ListExpression{
		Token: tok.Token{Type: tok.LPAREN, Lexeme: "("},
		Elements: []ast.Expression{
			&ast.SymbolLiteral{
				Token: tok.Token{Type: tok.SYMBOL, Lexeme: "/"},
				Value: "/",
			},
			&ast.IntegerLiteral{
				Token: tok.Token{Type: tok.INT, Lexeme: "12"},
				Value: 12,
			},
			&ast.IntegerLiteral{
				Token: tok.Token{Type: tok.INT, Lexeme: "3"},
				Value: 3,
			},
			&ast.IntegerLiteral{
				Token: tok.Token{Type: tok.INT, Lexeme: "2"},
				Value: 2,
			},
		},
	}

	// Create an environment and register built-in procedures
	env := obj.NewGlobalEnvironment()
	RegisterBuiltinProcedures(env)

	// Evaluate the expression
	result := Eval(divExpr, env)

	// Check if the result is an Integer object with the correct value
	integer, ok := result.(*obj.Integer)
	if !ok {
		t.Errorf("Eval didn't return Integer. Got %T", result)
		return
	}

	if integer.Value != 2 {
		t.Errorf("Eval returned wrong value for (/ 12 3 2). Got %d, want 2", integer.Value)
	}
}

// TestRationalInExpression tests the evaluation of expressions containing rational numbers
func TestRationalInExpression(t *testing.T) {
	// Create a list expression for (+ 1 3/2)
	expr := &ast.ListExpression{
		Elements: []ast.Expression{
			&ast.SymbolLiteral{Value: "+"},
			&ast.IntegerLiteral{Value: 1},
			&ast.RationalLiteral{Numerator: 3, Denominator: 2},
		},
	}

	// Create an environment and register built-in procedures
	env := obj.NewGlobalEnvironment()
	RegisterBuiltinProcedures(env)

	// Evaluate the expression
	result := Eval(expr, env)

	// Since we're mixing integer and rational, the result should be rational
	rational, ok := result.(*obj.Rational)
	if !ok {
		t.Errorf("Eval didn't return Rational. Got %T", result)
		return
	}

	// Check if the result is 5/2
	if rational.Inspect() != "5/2" {
		t.Errorf("rational.String() returned wrong value. Got %s, want 5/2", rational.Inspect())
	}
}

// TestRealInExpression tests the evaluation of expressions containing real numbers
func TestRealInExpression(t *testing.T) {
	// Create a list expression for (* 2 3.5)
	expr := &ast.ListExpression{
		Token: tok.Token{Type: tok.LPAREN, Lexeme: "("},
		Elements: []ast.Expression{
			&ast.SymbolLiteral{
				Token: tok.Token{Type: tok.SYMBOL, Lexeme: "*"},
				Value: "*",
			},
			&ast.IntegerLiteral{
				Token: tok.Token{Type: tok.INT, Lexeme: "2"},
				Value: 2,
			},
			&ast.RealLiteral{
				Token: tok.Token{Type: tok.REAL, Lexeme: "3.5"},
				Value: 3.5,
			},
		},
	}

	// Create an environment and register built-in procedures
	env := obj.NewGlobalEnvironment()
	RegisterBuiltinProcedures(env)

	// Evaluate the expression
	result := Eval(expr, env)

	// Since we're multiplying with a real, the result should be real
	real, ok := result.(*obj.Real)
	if !ok {
		t.Errorf("Eval didn't return Real. Got %T", result)
		return
	}

	if real.Value != 7.0 {
		t.Errorf("Eval returned wrong value for (* 2 3.5). Got %f, want 7.0", real.Value)
	}
}

// TestMixedNumberTypes tests the evaluation of expressions with mixed number types
func TestMixedNumberTypes(t *testing.T) {
	// Create a list expression for (+ 1 3/2 4.5)
	expr := &ast.ListExpression{
		Token: tok.Token{Type: tok.LPAREN, Lexeme: "("},
		Elements: []ast.Expression{
			&ast.SymbolLiteral{
				Token: tok.Token{Type: tok.SYMBOL, Lexeme: "+"},
				Value: "+",
			},
			&ast.IntegerLiteral{
				Token: tok.Token{Type: tok.INT, Lexeme: "1"},
				Value: 1,
			},
			&ast.RationalLiteral{
				Token:       tok.Token{Type: tok.RATIONAL, Lexeme: "3/2"},
				Numerator:   3,
				Denominator: 2,
			},
			&ast.RealLiteral{
				Token: tok.Token{Type: tok.REAL, Lexeme: "4.5"},
				Value: 4.5,
			},
		},
	}

	// Create an environment and register built-in procedures
	env := obj.NewGlobalEnvironment()
	RegisterBuiltinProcedures(env)

	// Evaluate the expression
	result := Eval(expr, env)

	// Since we have a real number, the result should be real
	real, ok := result.(*obj.Real)
	if !ok {
		t.Errorf("Eval didn't return Real. Got %T", result)
		return
	}

	if real.Value != 7.0 {
		t.Errorf("Eval returned wrong value for (+ 1 3/2 4.5). Got %f, want 7.0", real.Value)
	}
}

// TestDivisionByZero tests that division by zero returns an error
func TestDivisionByZero(t *testing.T) {
	// Create a list expression for (/ 10 0)
	expr := &ast.ListExpression{
		Token: tok.Token{Type: tok.LPAREN, Lexeme: "("},
		Elements: []ast.Expression{
			&ast.SymbolLiteral{
				Token: tok.Token{Type: tok.SYMBOL, Lexeme: "/"},
				Value: "/",
			},
			&ast.IntegerLiteral{
				Token: tok.Token{Type: tok.INT, Lexeme: "10"},
				Value: 10,
			},
			&ast.IntegerLiteral{
				Token: tok.Token{Type: tok.INT, Lexeme: "0"},
				Value: 0,
			},
		},
	}

	// Create an environment and register built-in procedures
	env := obj.NewGlobalEnvironment()
	RegisterBuiltinProcedures(env)

	// Evaluate the expression
	result := Eval(expr, env)

	// Check if the result is an Error object
	errObj, ok := result.(*obj.Error)
	if !ok {
		t.Errorf("Eval didn't return Error. Got %T", result)
		return
	}

	// Update error message check to match our new implementation
	if errObj.Message != "/ division by zero" {
		t.Errorf("Eval returned wrong error message. Got %s, want '/ division by zero'", errObj.Message)
		return
	}
}

// TestQuoteForm tests the quote special form
func TestQuoteForm(t *testing.T) {
	// Create a global environment
	globalEnv := obj.NewGlobalEnvironment()
	RegisterBuiltinProcedures(globalEnv)
	env := globalEnv.Environment

	// Test quoting a symbol
	symbolQuoteExpr := &ast.ListExpression{
		Token: tok.Token{Type: tok.LPAREN, Lexeme: "("},
		Elements: []ast.Expression{
			&ast.SymbolLiteral{Token: tok.Token{Type: tok.SYMBOL, Lexeme: "quote"}, Value: "quote"},
			&ast.SymbolLiteral{Token: tok.Token{Type: tok.SYMBOL, Lexeme: "hello"}, Value: "hello"},
		},
	}
	symbolResult := Eval(symbolQuoteExpr, env)
	symbolObj, ok := symbolResult.(*obj.Symbol)
	if !ok {
		t.Errorf("quote did not return Symbol. Got %T", symbolResult)
		return
	}
	if symbolObj.Value != "hello" {
		t.Errorf("quote returned wrong symbol value. Got %s, want hello", symbolObj.Value)
	}

	// Test quoting a list
	listQuoteExpr := &ast.ListExpression{
		Token: tok.Token{Type: tok.LPAREN, Lexeme: "("},
		Elements: []ast.Expression{
			&ast.SymbolLiteral{Token: tok.Token{Type: tok.SYMBOL, Lexeme: "quote"}, Value: "quote"},
			&ast.ListExpression{
				Token: tok.Token{Type: tok.LPAREN, Lexeme: "("},
				Elements: []ast.Expression{
					&ast.SymbolLiteral{Token: tok.Token{Type: tok.SYMBOL, Lexeme: "a"}, Value: "a"},
					&ast.IntegerLiteral{Token: tok.Token{Type: tok.INT, Lexeme: "1"}, Value: 1},
				},
			},
		},
	}
	listResult := Eval(listQuoteExpr, env)
	listObj, ok := listResult.(*obj.List)
	if !ok {
		t.Errorf("quote did not return List. Got %T", listResult)
		return
	}
	elements := listObj.ToSlice()
	if len(elements) != 2 {
		t.Errorf("quote returned list with wrong length. Got %d, want 2", len(elements))
		return
	}
	if sym, ok := elements[0].(*obj.Symbol); !ok || sym.Value != "a" {
		t.Errorf("quote returned list with wrong first element. Got %T(%v), want Symbol(a)", elements[0], elements[0])
	}
	if num, ok := elements[1].(*obj.Integer); !ok || num.Value != 1 {
		t.Errorf("quote returned list with wrong second element. Got %T(%v), want Integer(1)", elements[1], elements[1])
	}

	// Test quote with wrong number of arguments
	wrongArgsExpr := &ast.ListExpression{
		Token: tok.Token{Type: tok.LPAREN, Lexeme: "("},
		Elements: []ast.Expression{
			&ast.SymbolLiteral{Token: tok.Token{Type: tok.SYMBOL, Lexeme: "quote"}, Value: "quote"},
			&ast.SymbolLiteral{Token: tok.Token{Type: tok.SYMBOL, Lexeme: "a"}, Value: "a"},
			&ast.SymbolLiteral{Token: tok.Token{Type: tok.SYMBOL, Lexeme: "b"}, Value: "b"},
		},
	}
	wrongArgsResult := Eval(wrongArgsExpr, env)
	if errObj, ok := wrongArgsResult.(*obj.Error); !ok || errObj.Message != "quote requires exactly 1 argument" {
		t.Errorf("quote with wrong number of arguments did not return expected error. Got %T(%v)", wrongArgsResult, wrongArgsResult)
	}
}

// TestSetForm tests the set! special form
func TestSetForm(t *testing.T) {
	// Create a global environment and register built-in procedures
	globalEnv := obj.NewGlobalEnvironment()
	RegisterBuiltinProcedures(globalEnv)
	env := globalEnv

	// First define a variable to set
	dDefineExpr := &ast.ListExpression{
		Token: tok.Token{Type: tok.LPAREN, Lexeme: "("},
		Elements: []ast.Expression{
			&ast.SymbolLiteral{Token: tok.Token{Type: tok.SYMBOL, Lexeme: "define"}, Value: "define"},
			&ast.SymbolLiteral{Token: tok.Token{Type: tok.SYMBOL, Lexeme: "x"}, Value: "x"},
			&ast.IntegerLiteral{Token: tok.Token{Type: tok.INT, Lexeme: "10"}, Value: 10},
		},
	}
	Eval(dDefineExpr, env)

	// Verify the variable was defined
	getXExpr := &ast.SymbolLiteral{Token: tok.Token{Type: tok.SYMBOL, Lexeme: "x"}, Value: "x"}
	getXResult := Eval(getXExpr, env)
	getXInt, ok := getXResult.(*obj.Integer)
	if !ok {
		t.Errorf("x is not an Integer. Got %T", getXResult)
		return
	}
	if getXInt.Value != 10 {
		t.Errorf("x has wrong value after define. Got %d, want 10", getXInt.Value)
	}

	// Test setting a new value to an existing variable
	setExpr := &ast.ListExpression{
		Token: tok.Token{Type: tok.LPAREN, Lexeme: "("},
		Elements: []ast.Expression{
			&ast.SymbolLiteral{Token: tok.Token{Type: tok.SYMBOL, Lexeme: "set!"}, Value: "set!"},
			&ast.SymbolLiteral{Token: tok.Token{Type: tok.SYMBOL, Lexeme: "x"}, Value: "x"},
			&ast.IntegerLiteral{Token: tok.Token{Type: tok.INT, Lexeme: "20"}, Value: 20},
		},
	}
	setResult := Eval(setExpr, env)

	// Check if set! returns the new value
	integer, ok := setResult.(*obj.Integer)
	if !ok {
		t.Errorf("set! didn't return Integer. Got %T", setResult)
		return
	}
	if integer.Value != 20 {
		t.Errorf("set! returned wrong value. Got %d, want 20", integer.Value)
	}

	// Verify the value was actually changed in the environment
	getXResult = Eval(getXExpr, env)
	getXInt, ok = getXResult.(*obj.Integer)
	if !ok {
		t.Errorf("x is not an Integer. Got %T", getXResult)
		return
	}
	if getXInt.Value != 20 {
		t.Errorf("x has wrong value after set!. Got %d, want 20", getXInt.Value)
	}

	// Test setting a non-existent variable (should return error)
	setNonExistentExpr := &ast.ListExpression{
		Token: tok.Token{Type: tok.LPAREN, Lexeme: "("},
		Elements: []ast.Expression{
			&ast.SymbolLiteral{Token: tok.Token{Type: tok.SYMBOL, Lexeme: "set!"}, Value: "set!"},
			&ast.SymbolLiteral{Token: tok.Token{Type: tok.SYMBOL, Lexeme: "y"}, Value: "y"},
			&ast.IntegerLiteral{Token: tok.Token{Type: tok.INT, Lexeme: "30"}, Value: 30},
		},
	}
	setNonExistentResult := Eval(setNonExistentExpr, env)
	errObj, ok := setNonExistentResult.(*obj.Error)
	if !ok {
		t.Errorf("set! on non-existent variable didn't return Error. Got %T", setNonExistentResult)
		return
	}
	if errObj.Message != "set!: undefined variable: y" {
		t.Errorf("set! returned wrong error message. Got %s, want 'set!: undefined variable: y'", errObj.Message)
	}
}

// evalString evaluates a Scheme expression from a string and returns the result
func evalString(expr string, env obj.Environment) (obj.Object, error) {
	// Create a lexer
	l := lex.NewLexer(expr)

	// Create a parser
	p := par.NewParser(l)

	// Parse the expression
	astExpr, err := p.ParseExpression()
	if err != nil {
		return nil, err
	}

	// Evaluate the expression
	result := Eval(astExpr, env)

	// Check if the result is an error object
	if errObj, ok := result.(*obj.Error); ok {
		// Return nil for the object and the error message as a Go error
		return nil, fmt.Errorf("%s", errObj.Message)
	}

	return result, nil
}

// TestCase represents a single test case for evaluating a Scheme expression
type TestCase struct {
	Expr     string      // The Scheme expression to evaluate
	Expected interface{} // The expected result (int64, float64, string, etc.)
	// For complex types like rational numbers or booleans
	ExpectInteger    bool   // Set to true if expecting an integer
	ExpectRational   bool   // Set to true if expecting a rational number
	ExpectReal       bool   // Set to true if expecting a real number
	ExpectBoolean    bool   // Set to true if expecting a boolean
	ExpectError      bool   // Set to true if expecting an error
	ExpectedErrorMsg string // Expected error message if ExpectError is true
}

// runTestCases runs a series of test cases
func runTestCases(t *testing.T, cases []TestCase) {
	// Create a global environment and register built-in procedures
	globalEnv := obj.NewGlobalEnvironment()
	RegisterBuiltinProcedures(globalEnv)
	env := globalEnv

	// Run each test case
	for i, tc := range cases {
		// Evaluate the expression
		result, err := evalString(tc.Expr, env)

		// Check for parsing errors
		if err != nil && !tc.ExpectError {
			t.Errorf("Test case %d (%s): Unexpected parse error: %s", i+1, tc.Expr, err)
			continue
		}

		// Check if we expected an error
		if tc.ExpectError {
			if err == nil {
				t.Errorf("Test case %d (%s): Expected an error, but got nil", i+1, tc.Expr)
				continue
			}

			// Check if we have an expected error message
			if tc.ExpectedErrorMsg != "" {
				// We can't check for specific error messages because we converted to Go error
				// But we can still verify that we got an error
				t.Logf("Test case %d (%s): Got error as expected: %s", i+1, tc.Expr, err)
			}
			continue
		}

		// Check result types and values
		switch {
		case tc.ExpectInteger:
			integer, ok := result.(*obj.Integer)
			if !ok {
				t.Errorf("Test case %d (%s): Expected Integer, got %T", i+1, tc.Expr, result)
				continue
			}
			if expected, ok := tc.Expected.(int64); ok && integer.Value != expected {
				t.Errorf("Test case %d (%s): Expected %d, got %d", i+1, tc.Expr, expected, integer.Value)
			}
		case tc.ExpectRational:
			rational, ok := result.(*obj.Rational)
			if !ok {
				t.Errorf("Test case %d (%s): Expected Rational, got %T", i+1, tc.Expr, result)
				continue
			}
			if expected, ok := tc.Expected.(string); ok {
				rationalStr := rational.Inspect()
				if rationalStr != expected {
					t.Errorf("Test case %d (%s): Expected %s, got %s", i+1, tc.Expr, expected, rationalStr)
				}
			}
		case tc.ExpectReal:
			real, ok := result.(*obj.Real)
			if !ok {
				t.Errorf("Test case %d (%s): Expected Real, got %T", i+1, tc.Expr, result)
				continue
			}
			if expected, ok := tc.Expected.(float64); ok && real.Value != expected {
				t.Errorf("Test case %d (%s): Expected %f, got %f", i+1, tc.Expr, expected, real.Value)
			}
		case tc.ExpectBoolean:
			boolean, ok := result.(*obj.Boolean)
			if !ok {
				t.Errorf("Test case %d (%s): Expected Boolean, got %T", i+1, tc.Expr, result)
				continue
			}
			if expected, ok := tc.Expected.(bool); ok && boolean.Value != expected {
				t.Errorf("Test case %d (%s): Expected %v, got %v", i+1, tc.Expr, expected, boolean.Value)
			}
		default:
			// This is a basic test where we just check if the evaluation succeeded
		}
	}
}

// TestEvalString tests the evalString helper function
func TestEvalString(t *testing.T) {
	// Create a global environment and register built-in procedures
	globalEnv := obj.NewGlobalEnvironment()
	RegisterBuiltinProcedures(globalEnv)
	env := globalEnv

	// Test integer literal
	result, err := evalString("42", env)
	if err != nil {
		t.Errorf("evalString failed for integer literal: %s", err)
		return
	}
	integer, ok := result.(*obj.Integer)
	if !ok {
		t.Errorf("Result is not an Integer. Got %T", result)
		return
	}
	if integer.Value != 42 {
		t.Errorf("Integer value is incorrect. Got %d, want %d", integer.Value, 42)
	}
}

// TestExpressionSuite runs a suite of expression tests
func TestExpressionSuite(t *testing.T) {
	testCases := []TestCase{
		// Basic arithmetic
		{Expr: "42", Expected: int64(42), ExpectInteger: true},
		{Expr: "(+ 1 2)", Expected: int64(3), ExpectInteger: true},
		{Expr: "(- 5 3)", Expected: int64(2), ExpectInteger: true},
		{Expr: "(* 4 5)", Expected: int64(20), ExpectInteger: true},
		{Expr: "(/ 10 2)", Expected: int64(5), ExpectInteger: true},

		// Rational numbers
		{Expr: "1/2", Expected: "1/2", ExpectRational: true},
		{Expr: "(+ 1/2 1/2)", Expected: "1/1", ExpectRational: true}, // 1/2 + 1/2 = 1/1
		{Expr: "(+ 1 1/2)", Expected: "3/2", ExpectRational: true},

		// Real numbers
		{Expr: "3.14", Expected: 3.14, ExpectReal: true},
		{Expr: "(* 2 3.5)", Expected: 7.0, ExpectReal: true},

		// Nested expressions
		{Expr: "(+ (* 2 3) (- 5 1))", Expected: int64(10), ExpectInteger: true},
		{Expr: "(+ 1 2 3 4 5)", Expected: int64(15), ExpectInteger: true},

		// Define and set!
		{Expr: "(define x 10)", Expected: nil}, // define returns nothing
		{Expr: "x", Expected: int64(10), ExpectInteger: true},
		{Expr: "(set! x 20)", Expected: int64(20), ExpectInteger: true},
		{Expr: "x", Expected: int64(20), ExpectInteger: true},
		{Expr: "(set! y 30)", ExpectError: true}, // Just check for error, not specific message
	}

	runTestCases(t, testCases)
}

// TestSymbolComparisonFunctions tests the eq?, eqv?, and equal? functions
func TestSymbolComparisonFunctions(t *testing.T) {
	// Create a global environment and register built-in procedures
	globalEnv := obj.NewGlobalEnvironment()
	RegisterBuiltinProcedures(globalEnv)
	env := globalEnv.Environment

	// Create symbol objects for testing
	sym1 := &obj.Symbol{Value: "test"}
	sym2 := &obj.Symbol{Value: "test"}
	sym3 := &obj.Symbol{Value: "different"}

	// Add symbols to the environment
	env.Set("sym1", sym1)
	env.Set("sym2", sym2)
	env.Set("sym3", sym3)

	// Test eq? function
	eqTestExpr := &ast.ListExpression{
		Token: tok.Token{Type: tok.LPAREN, Lexeme: "("},
		Elements: []ast.Expression{
			&ast.SymbolLiteral{Token: tok.Token{Type: tok.SYMBOL, Lexeme: "eq?"}, Value: "eq?"},
			&ast.SymbolLiteral{Token: tok.Token{Type: tok.SYMBOL, Lexeme: "sym1"}, Value: "sym1"},
			&ast.SymbolLiteral{Token: tok.Token{Type: tok.SYMBOL, Lexeme: "sym2"}, Value: "sym2"},
		},
	}
	eqResult := Eval(eqTestExpr, env)
	eqBool, ok := eqResult.(*obj.Boolean)
	if !ok {
		t.Errorf("eq? did not return Boolean. Got %T", eqResult)
		return
	}
	if !eqBool.Value {
		t.Errorf("eq? should return #t for identical symbols, got #f")
	}

	// Test eq? with different symbols
	eqDifferentExpr := &ast.ListExpression{
		Token: tok.Token{Type: tok.LPAREN, Lexeme: "("},
		Elements: []ast.Expression{
			&ast.SymbolLiteral{Token: tok.Token{Type: tok.SYMBOL, Lexeme: "eq?"}, Value: "eq?"},
			&ast.SymbolLiteral{Token: tok.Token{Type: tok.SYMBOL, Lexeme: "sym1"}, Value: "sym1"},
			&ast.SymbolLiteral{Token: tok.Token{Type: tok.SYMBOL, Lexeme: "sym3"}, Value: "sym3"},
		},
	}
	eqDifferentResult := Eval(eqDifferentExpr, env)
	eqDifferentBool, ok := eqDifferentResult.(*obj.Boolean)
	if !ok {
		t.Errorf("eq? did not return Boolean. Got %T", eqDifferentResult)
		return
	}
	if eqDifferentBool.Value {
		t.Errorf("eq? should return #f for different symbols, got #t")
	}

	// Test eqv? function
	eqvTestExpr := &ast.ListExpression{
		Token: tok.Token{Type: tok.LPAREN, Lexeme: "("},
		Elements: []ast.Expression{
			&ast.SymbolLiteral{Token: tok.Token{Type: tok.SYMBOL, Lexeme: "eqv?"}, Value: "eqv?"},
			&ast.SymbolLiteral{Token: tok.Token{Type: tok.SYMBOL, Lexeme: "sym1"}, Value: "sym1"},
			&ast.SymbolLiteral{Token: tok.Token{Type: tok.SYMBOL, Lexeme: "sym2"}, Value: "sym2"},
		},
	}
	eqvResult := Eval(eqvTestExpr, env)
	eqvBool, ok := eqvResult.(*obj.Boolean)
	if !ok {
		t.Errorf("eqv? did not return Boolean. Got %T", eqvResult)
		return
	}
	if !eqvBool.Value {
		t.Errorf("eqv? should return #t for identical symbols, got #f")
	}

	// Test equal? function
	equalTestExpr := &ast.ListExpression{
		Token: tok.Token{Type: tok.LPAREN, Lexeme: "("},
		Elements: []ast.Expression{
			&ast.SymbolLiteral{Token: tok.Token{Type: tok.SYMBOL, Lexeme: "equal?"}, Value: "equal?"},
			&ast.SymbolLiteral{Token: tok.Token{Type: tok.SYMBOL, Lexeme: "sym1"}, Value: "sym1"},
			&ast.SymbolLiteral{Token: tok.Token{Type: tok.SYMBOL, Lexeme: "sym2"}, Value: "sym2"},
		},
	}
	equalResult := Eval(equalTestExpr, env)
	equalBool, ok := equalResult.(*obj.Boolean)
	if !ok {
		t.Errorf("equal? did not return Boolean. Got %T", equalResult)
		if errObj, isError := equalResult.(*obj.Error); isError {
			t.Errorf("Error message: %s", errObj.Message)
		}
		return
	}
	if !equalBool.Value {
		t.Errorf("equal? should return #t for identical symbols, got #f")
	}
}

// TestBooleanLiterals tests the evaluation of boolean literals
func TestBooleanLiterals(t *testing.T) {
	// Create boolean expressions #t and #f
	exprTrue := &ast.BooleanLiteral{
		Token: tok.Token{Type: tok.BOOLEAN, Lexeme: "#t"},
		Value: true,
	}
	exprFalse := &ast.BooleanLiteral{
		Token: tok.Token{Type: tok.BOOLEAN, Lexeme: "#f"},
		Value: false,
	}

	// Create an environment
	env := obj.NewGlobalEnvironment()

	// Test #t
	resultTrue := Eval(exprTrue, env)
	boolTrue, ok := resultTrue.(*obj.Boolean)
	if !ok {
		t.Errorf("Eval didn't return Boolean. Got %T", resultTrue)
		return
	}
	if !boolTrue.Value {
		t.Errorf("Eval returned wrong boolean value. Got false, want true")
	}

	// Test #f
	resultFalse := Eval(exprFalse, env)
	boolFalse, ok := resultFalse.(*obj.Boolean)
	if !ok {
		t.Errorf("Eval didn't return Boolean. Got %T", resultFalse)
		return
	}
	if boolFalse.Value {
		t.Errorf("Eval returned wrong boolean value. Got true, want false")
	}
}

// TestNotFunction tests the not built-in function
func TestNotFunction(t *testing.T) {
	// Create a global environment and register built-in procedures
	globalEnv := obj.NewGlobalEnvironment()
	RegisterBuiltinProcedures(globalEnv)
	env := globalEnv.Environment

	// Test not #t -> #f
	notTrueExpr := &ast.ListExpression{
		Token: tok.Token{Type: tok.LPAREN, Lexeme: "("},
		Elements: []ast.Expression{
			&ast.SymbolLiteral{Token: tok.Token{Type: tok.SYMBOL, Lexeme: "not"}, Value: "not"},
			&ast.BooleanLiteral{Token: tok.Token{Type: tok.BOOLEAN, Lexeme: "#t"}, Value: true},
		},
	}
	resultNotTrue := Eval(notTrueExpr, env)
	boolNotTrue, ok := resultNotTrue.(*obj.Boolean)
	if !ok {
		t.Errorf("Eval didn't return Boolean. Got %T", resultNotTrue)
		return
	}
	if boolNotTrue.Value {
		t.Errorf("not #t should return #f, got #t")
	}

	// Test not #f -> #t
	notFalseExpr := &ast.ListExpression{
		Token: tok.Token{Type: tok.LPAREN, Lexeme: "("},
		Elements: []ast.Expression{
			&ast.SymbolLiteral{Token: tok.Token{Type: tok.SYMBOL, Lexeme: "not"}, Value: "not"},
			&ast.BooleanLiteral{Token: tok.Token{Type: tok.BOOLEAN, Lexeme: "#f"}, Value: false},
		},
	}
	resultNotFalse := Eval(notFalseExpr, env)
	boolNotFalse, ok := resultNotFalse.(*obj.Boolean)
	if !ok {
		t.Errorf("Eval didn't return Boolean. Got %T", resultNotFalse)
		return
	}
	if !boolNotFalse.Value {
		t.Errorf("not #f should return #t, got #f")
	}

	// Test not with non-boolean argument (not 10)
	notNonBooleanExpr := &ast.ListExpression{
		Token: tok.Token{Type: tok.LPAREN, Lexeme: "("},
		Elements: []ast.Expression{
			&ast.SymbolLiteral{Token: tok.Token{Type: tok.SYMBOL, Lexeme: "not"}, Value: "not"},
			&ast.IntegerLiteral{Token: tok.Token{Type: tok.INT, Lexeme: "10"}, Value: 10},
		},
	}
	resultNotNonBoolean := Eval(notNonBooleanExpr, env)
	boolNotNonBoolean, ok := resultNotNonBoolean.(*obj.Boolean)
	if !ok {
		t.Errorf("Eval didn't return Boolean. Got %T", resultNotNonBoolean)
		return
	}
	if boolNotNonBoolean.Value {
		t.Errorf("not 10 should return #f (since 10 is truthy), got #t")
	}
}

// TestAndFunction tests the and built-in function
func TestAndFunction(t *testing.T) {
	// Create a global environment and register built-in procedures
	globalEnv := obj.NewGlobalEnvironment()
	RegisterBuiltinProcedures(globalEnv)
	env := globalEnv.Environment

	// Test and with no arguments -> #t
	andEmptyExpr := &ast.ListExpression{
		Token: tok.Token{Type: tok.LPAREN, Lexeme: "("},
		Elements: []ast.Expression{
			&ast.SymbolLiteral{Token: tok.Token{Type: tok.SYMBOL, Lexeme: "and"}, Value: "and"},
		},
	}
	resultAndEmpty := Eval(andEmptyExpr, env)
	boolAndEmpty, ok := resultAndEmpty.(*obj.Boolean)
	if !ok {
		t.Errorf("Eval didn't return Boolean. Got %T", resultAndEmpty)
		return
	}
	if !boolAndEmpty.Value {
		t.Errorf("and with no arguments should return #t, got #f")
	}

	// Test and #t #t -> #t
	andTTEpr := &ast.ListExpression{
		Token: tok.Token{Type: tok.LPAREN, Lexeme: "("},
		Elements: []ast.Expression{
			&ast.SymbolLiteral{Token: tok.Token{Type: tok.SYMBOL, Lexeme: "and"}, Value: "and"},
			&ast.BooleanLiteral{Token: tok.Token{Type: tok.BOOLEAN, Lexeme: "#t"}, Value: true},
			&ast.BooleanLiteral{Token: tok.Token{Type: tok.BOOLEAN, Lexeme: "#t"}, Value: true},
		},
	}
	resultAndTT := Eval(andTTEpr, env)
	boolAndTT, ok := resultAndTT.(*obj.Boolean)
	if !ok {
		t.Errorf("Eval didn't return Boolean. Got %T", resultAndTT)
		return
	}
	if !boolAndTT.Value {
		t.Errorf("and #t #t should return #t, got #f")
	}

	// Test and #t #f -> #f
	andTFExpr := &ast.ListExpression{
		Token: tok.Token{Type: tok.LPAREN, Lexeme: "("},
		Elements: []ast.Expression{
			&ast.SymbolLiteral{Token: tok.Token{Type: tok.SYMBOL, Lexeme: "and"}, Value: "and"},
			&ast.BooleanLiteral{Token: tok.Token{Type: tok.BOOLEAN, Lexeme: "#t"}, Value: true},
			&ast.BooleanLiteral{Token: tok.Token{Type: tok.BOOLEAN, Lexeme: "#f"}, Value: false},
		},
	}
	resultAndTF := Eval(andTFExpr, env)
	boolAndTF, ok := resultAndTF.(*obj.Boolean)
	if !ok {
		t.Errorf("Eval didn't return Boolean. Got %T", resultAndTF)
		return
	}
	if boolAndTF.Value {
		t.Errorf("and #t #f should return #f, got #t")
	}

	// Test and with non-boolean arguments (and #t 42)
	andNonBooleanExpr := &ast.ListExpression{
		Token: tok.Token{Type: tok.LPAREN, Lexeme: "("},
		Elements: []ast.Expression{
			&ast.SymbolLiteral{Token: tok.Token{Type: tok.SYMBOL, Lexeme: "and"}, Value: "and"},
			&ast.BooleanLiteral{Token: tok.Token{Type: tok.BOOLEAN, Lexeme: "#t"}, Value: true},
			&ast.IntegerLiteral{Token: tok.Token{Type: tok.INT, Lexeme: "42"}, Value: 42},
		},
	}
	resultAndNonBoolean := Eval(andNonBooleanExpr, env)
	intAndNonBoolean, ok := resultAndNonBoolean.(*obj.Integer)
	if !ok {
		t.Errorf("Eval didn't return Integer. Got %T", resultAndNonBoolean)
		return
	}
	if intAndNonBoolean.Value != 42 {
		t.Errorf("and #t 42 should return 42, got %d", intAndNonBoolean.Value)
	}
}

// TestOrFunction tests the or built-in function
func TestOrFunction(t *testing.T) {
	// Create a global environment and register built-in procedures
	globalEnv := obj.NewGlobalEnvironment()
	RegisterBuiltinProcedures(globalEnv)
	env := globalEnv.Environment

	// Test or with no arguments -> #f
	orEmptyExpr := &ast.ListExpression{
		Token: tok.Token{Type: tok.LPAREN, Lexeme: "("},
		Elements: []ast.Expression{
			&ast.SymbolLiteral{Token: tok.Token{Type: tok.SYMBOL, Lexeme: "or"}, Value: "or"},
		},
	}
	resultOrEmpty := Eval(orEmptyExpr, env)
	boolOrEmpty, ok := resultOrEmpty.(*obj.Boolean)
	if !ok {
		t.Errorf("Eval didn't return Boolean. Got %T", resultOrEmpty)
		return
	}
	if boolOrEmpty.Value {
		t.Errorf("or with no arguments should return #f, got #t")
	}

	// Test or #f #f -> #f
	orFFExpr := &ast.ListExpression{
		Token: tok.Token{Type: tok.LPAREN, Lexeme: "("},
		Elements: []ast.Expression{
			&ast.SymbolLiteral{Token: tok.Token{Type: tok.SYMBOL, Lexeme: "or"}, Value: "or"},
			&ast.BooleanLiteral{Token: tok.Token{Type: tok.BOOLEAN, Lexeme: "#f"}, Value: false},
			&ast.BooleanLiteral{Token: tok.Token{Type: tok.BOOLEAN, Lexeme: "#f"}, Value: false},
		},
	}
	resultOrFF := Eval(orFFExpr, env)
	boolOrFF, ok := resultOrFF.(*obj.Boolean)
	if !ok {
		t.Errorf("Eval didn't return Boolean. Got %T", resultOrFF)
		return
	}
	if boolOrFF.Value {
		t.Errorf("or #f #f should return #f, got #t")
	}

	// Test or #f #t -> #t
	orFTExpr := &ast.ListExpression{
		Token: tok.Token{Type: tok.LPAREN, Lexeme: "("},
		Elements: []ast.Expression{
			&ast.SymbolLiteral{Token: tok.Token{Type: tok.SYMBOL, Lexeme: "or"}, Value: "or"},
			&ast.BooleanLiteral{Token: tok.Token{Type: tok.BOOLEAN, Lexeme: "#f"}, Value: false},
			&ast.BooleanLiteral{Token: tok.Token{Type: tok.BOOLEAN, Lexeme: "#t"}, Value: true},
		},
	}
	resultOrFT := Eval(orFTExpr, env)
	boolOrFT, ok := resultOrFT.(*obj.Boolean)
	if !ok {
		t.Errorf("Eval didn't return Boolean. Got %T", resultOrFT)
		return
	}
	if !boolOrFT.Value {
		t.Errorf("or #f #t should return #t, got #f")
	}

	// Test or with non-boolean arguments (or #f 42)
	orNonBooleanExpr := &ast.ListExpression{
		Token: tok.Token{Type: tok.LPAREN, Lexeme: "("},
		Elements: []ast.Expression{
			&ast.SymbolLiteral{Token: tok.Token{Type: tok.SYMBOL, Lexeme: "or"}, Value: "or"},
			&ast.BooleanLiteral{Token: tok.Token{Type: tok.BOOLEAN, Lexeme: "#f"}, Value: false},
			&ast.IntegerLiteral{Token: tok.Token{Type: tok.INT, Lexeme: "42"}, Value: 42},
		},
	}
	resultOrNonBoolean := Eval(orNonBooleanExpr, env)
	intOrNonBoolean, ok := resultOrNonBoolean.(*obj.Integer)
	if !ok {
		t.Errorf("Eval didn't return Integer. Got %T", resultOrNonBoolean)
		return
	}
	if intOrNonBoolean.Value != 42 {
		t.Errorf("or #f 42 should return 42, got %d", intOrNonBoolean.Value)
	}
}

// TestUnknownOperator tests that an unknown operator returns an error
func TestUnknownOperator(t *testing.T) {
	// Create a list expression with an unknown operator
	expr := &ast.ListExpression{
		Token: tok.Token{Type: tok.LPAREN, Lexeme: "("},
		Elements: []ast.Expression{
			&ast.SymbolLiteral{
				Token: tok.Token{Type: tok.SYMBOL, Lexeme: "unknown"},
				Value: "unknown",
			},
			&ast.IntegerLiteral{
				Token: tok.Token{Type: tok.INT, Lexeme: "1"},
				Value: 1,
			},
		},
	}

	// Create an environment
	env := obj.NewGlobalEnvironment()

	// Evaluate the expression
	result := Eval(expr, env)

	// Check if the result is an Error object
	errObj, ok := result.(*obj.Error)
	if !ok {
		t.Errorf("Eval didn't return Error. Got %T", result)
		return
	}

	if errObj.Message != "unknown symbol: unknown" {
		t.Errorf("Eval returned wrong error message. Got %s, want 'unknown symbol: unknown'", errObj.Message)
	}
}

// TestDefineVariable tests the evaluation of define expressions for variable definition
func TestDefineVariable(t *testing.T) {
	// Create a define expression for (define x 42)
	defineExpr := &ast.ListExpression{
		Token: tok.Token{Type: tok.LPAREN, Lexeme: "("},
		Elements: []ast.Expression{
			&ast.SymbolLiteral{
				Token: tok.Token{Type: tok.SYMBOL, Lexeme: "define"},
				Value: "define",
			},
			&ast.SymbolLiteral{
				Token: tok.Token{Type: tok.SYMBOL, Lexeme: "x"},
				Value: "x",
			},
			&ast.IntegerLiteral{
				Token: tok.Token{Type: tok.INT, Lexeme: "42"},
				Value: 42,
			},
		},
	}

	// Create an environment
	globalEnv := obj.NewGlobalEnvironment()
	RegisterBuiltinProcedures(globalEnv)
	env := globalEnv

	// Evaluate the define expression
	result := Eval(defineExpr, env)

	// Check if the result is an Integer object with the correct value (define should return the defined value)
	integer, ok := result.(*obj.Integer)
	if !ok {
		t.Errorf("Eval of define didn't return Integer. Got %T", result)
		return
	}

	if integer.Value != 42 {
		t.Errorf("Eval of define returned wrong value. Got %d, want 42", integer.Value)
	}

	// Check if the variable was correctly set in the environment
	xValue, ok := env.Get("x")
	if !ok {
		t.Errorf("variable 'x' was not defined in the environment")
		return
	}

	xInt, ok := xValue.(*obj.Integer)
	if !ok {
		t.Errorf("variable 'x' has wrong type. Got %T, want *obj.Integer", xValue)
		return
	}

	if xInt.Value != 42 {
		t.Errorf("variable 'x' has wrong value. Got %d, want 42", xInt.Value)
	}
}

// TestDefineProcedure tests the evaluation of define expressions for procedure definition
func TestDefineProcedure(t *testing.T) {
	// Create a define expression for (define (add x y) (+ x y))
	defineExpr := &ast.ListExpression{
		Token: tok.Token{Type: tok.LPAREN, Lexeme: "("},
		Elements: []ast.Expression{
			&ast.SymbolLiteral{
				Token: tok.Token{Type: tok.SYMBOL, Lexeme: "define"},
				Value: "define",
			},
			// Procedure signature (add x y)
			&ast.ListExpression{
				Token: tok.Token{Type: tok.LPAREN, Lexeme: "("},
				Elements: []ast.Expression{
					&ast.SymbolLiteral{
						Token: tok.Token{Type: tok.SYMBOL, Lexeme: "add"},
						Value: "add",
					},
					&ast.SymbolLiteral{
						Token: tok.Token{Type: tok.SYMBOL, Lexeme: "x"},
						Value: "x",
					},
					&ast.SymbolLiteral{
						Token: tok.Token{Type: tok.SYMBOL, Lexeme: "y"},
						Value: "y",
					},
				},
			},
			// Procedure body (+ x y)
			&ast.ListExpression{
				Token: tok.Token{Type: tok.LPAREN, Lexeme: "("},
				Elements: []ast.Expression{
					&ast.SymbolLiteral{
						Token: tok.Token{Type: tok.SYMBOL, Lexeme: "+"},
						Value: "+",
					},
					&ast.SymbolLiteral{
						Token: tok.Token{Type: tok.SYMBOL, Lexeme: "x"},
						Value: "x",
					},
					&ast.SymbolLiteral{
						Token: tok.Token{Type: tok.SYMBOL, Lexeme: "y"},
						Value: "y",
					},
				},
			},
		},
	}

	// Create an environment and register built-in procedures
	globalEnv := obj.NewGlobalEnvironment()
	RegisterBuiltinProcedures(globalEnv)
	env := globalEnv

	// Evaluate the define expression
	result := Eval(defineExpr, env)

	// Check if the result is a Procedure object
	proc, ok := result.(*obj.Procedure)
	if !ok {
		t.Errorf("Eval of define for procedure didn't return Procedure. Got %T", result)
		if err, isError := result.(*obj.Error); isError {
			t.Errorf("Error message: %s", err.Message)
		}
		return
	}

	// Check if the procedure name is correct
	if proc.Name != "add" {
		t.Errorf("Procedure has wrong name. Got %s, want 'add'", proc.Name)
	}

	// Check if the procedure parameters are correct
	if len(proc.Params) != 2 || proc.Params[0] != "x" || proc.Params[1] != "y" {
		t.Errorf("Procedure has wrong parameters. Got %v, want ['x', 'y']", proc.Params)
	}

	// Check if the procedure was correctly set in the environment
	addValue, ok := env.Get("add")
	if !ok {
		t.Errorf("procedure 'add' was not defined in the environment")
		return
	}

	if _, ok := addValue.(*obj.Procedure); !ok {
		t.Errorf("procedure 'add' has wrong type. Got %T, want *obj.Procedure", addValue)
		return
	}

	// Call the defined procedure to verify it works
	// Create a call expression (add 10 20)
	callExpr := &ast.ListExpression{
		Token: tok.Token{Type: tok.LPAREN, Lexeme: "("},
		Elements: []ast.Expression{
			&ast.SymbolLiteral{
				Token: tok.Token{Type: tok.SYMBOL, Lexeme: "add"},
				Value: "add",
			},
			&ast.IntegerLiteral{
				Token: tok.Token{Type: tok.INT, Lexeme: "10"},
				Value: 10,
			},
			&ast.IntegerLiteral{
				Token: tok.Token{Type: tok.INT, Lexeme: "20"},
				Value: 20,
			},
		},
	}

	// Evaluate the call expression
	callResult := Eval(callExpr, env)

	// Check if the result is an Integer object with the correct value
	resultInt, ok := callResult.(*obj.Integer)
	if !ok {
		t.Errorf("Call to defined procedure didn't return Integer. Got %T", callResult)
		return
	}

	if resultInt.Value != 30 {
		t.Errorf("Call to defined procedure returned wrong value. Got %d, want 30", resultInt.Value)
	}
}

// TestDefineErrorInvalidSyntax tests that define with invalid syntax returns an error
func TestDefineErrorInvalidSyntax(t *testing.T) {
	// Create a define expression with invalid syntax (too few arguments)
	defineExpr := &ast.ListExpression{
		Token: tok.Token{Type: tok.LPAREN, Lexeme: "("},
		Elements: []ast.Expression{
			&ast.SymbolLiteral{
				Token: tok.Token{Type: tok.SYMBOL, Lexeme: "define"},
				Value: "define",
			},
			// Missing value
		},
	}

	// Create an environment and register built-in procedures
	globalEnv := obj.NewGlobalEnvironment()
	RegisterBuiltinProcedures(globalEnv)
	env := globalEnv

	// Evaluate the define expression
	result := Eval(defineExpr, env)

	// Check if the result is an Error object
	errObj, ok := result.(*obj.Error)
	if !ok {
		t.Errorf("Eval of define with invalid syntax didn't return Error. Got %T", result)
		return
	}

	// Check if the error message is correct
	if errObj.Message != "define requires at least 2 arguments" {
		t.Errorf("Eval of define with invalid syntax returned wrong error message. Got %s, want 'define requires at least 2 arguments'", errObj.Message)
	}
}

// Additional tests for the modified builtin functions to support multiple arguments

// TestAddZeroArguments tests that (+) returns 0
func TestAddZeroArguments(t *testing.T) {
	// Create a list expression for (+)
	addExpr := &ast.ListExpression{
		Token: tok.Token{Type: tok.LPAREN, Lexeme: "("},
		Elements: []ast.Expression{
			&ast.SymbolLiteral{
				Token: tok.Token{Type: tok.SYMBOL, Lexeme: "+"},
				Value: "+",
			},
		},
	}

	// Create an environment and register built-in procedures
	env := obj.NewGlobalEnvironment()
	RegisterBuiltinProcedures(env)

	// Evaluate the expression
	result := Eval(addExpr, env)

	// Check if the result is an Integer object with value 0
	integer, ok := result.(*obj.Integer)
	if !ok {
		t.Errorf("Eval didn't return Integer. Got %T", result)
		return
	}

	if integer.Value != 0 {
		t.Errorf("Eval returned wrong value for (+). Got %d, want 0", integer.Value)
	}
}

// TestMultiplyZeroArguments tests that (*) returns 1
func TestMultiplyZeroArguments(t *testing.T) {
	// Create a list expression for (*)
	mulExpr := &ast.ListExpression{
		Token: tok.Token{Type: tok.LPAREN, Lexeme: "("},
		Elements: []ast.Expression{
			&ast.SymbolLiteral{
				Token: tok.Token{Type: tok.SYMBOL, Lexeme: "*"},
				Value: "*",
			},
		},
	}

	// Create an environment and register built-in procedures
	env := obj.NewGlobalEnvironment()
	RegisterBuiltinProcedures(env)

	// Evaluate the expression
	result := Eval(mulExpr, env)

	// Check if the result is an Integer object with value 1
	integer, ok := result.(*obj.Integer)
	if !ok {
		t.Errorf("Eval didn't return Integer. Got %T", result)
		return
	}

	if integer.Value != 1 {
		t.Errorf("Eval returned wrong value for (*). Got %d, want 1", integer.Value)
	}
}

// TestSubtractSingleArgument tests that (- x) returns -x
func TestSubtractSingleArgument(t *testing.T) {
	// Create a list expression for (- 42)
	subExpr := &ast.ListExpression{
		Token: tok.Token{Type: tok.LPAREN, Lexeme: "("},
		Elements: []ast.Expression{
			&ast.SymbolLiteral{
				Token: tok.Token{Type: tok.SYMBOL, Lexeme: "-"},
				Value: "-",
			},
			&ast.IntegerLiteral{
				Token: tok.Token{Type: tok.INT, Lexeme: "42"},
				Value: 42,
			},
		},
	}

	// Create an environment and register built-in procedures
	env := obj.NewGlobalEnvironment()
	RegisterBuiltinProcedures(env)

	// Evaluate the expression
	result := Eval(subExpr, env)

	// Check if the result is an Integer object with value -42
	integer, ok := result.(*obj.Integer)
	if !ok {
		t.Errorf("Eval didn't return Integer. Got %T", result)
		return
	}

	if integer.Value != -42 {
		t.Errorf("Eval returned wrong value for (- 42). Got %d, want -42", integer.Value)
	}
}

// TestDivideSingleArgument tests that (/ x) returns 1/x
func TestDivideSingleArgument(t *testing.T) {
	// Create a list expression for (/ 2)
	divExpr := &ast.ListExpression{
		Token: tok.Token{Type: tok.LPAREN, Lexeme: "("},
		Elements: []ast.Expression{
			&ast.SymbolLiteral{
				Token: tok.Token{Type: tok.SYMBOL, Lexeme: "/"},
				Value: "/",
			},
			&ast.IntegerLiteral{
				Token: tok.Token{Type: tok.INT, Lexeme: "2"},
				Value: 2,
			},
		},
	}

	// Create an environment and register built-in procedures
	env := obj.NewGlobalEnvironment()
	RegisterBuiltinProcedures(env)

	// Evaluate the expression
	result := Eval(divExpr, env)

	// Check if the result is a Rational object with value 1/2
	rational, ok := result.(*obj.Rational)
	if !ok {
		t.Errorf("Eval didn't return Rational. Got %T", result)
		return
	}

	if rational.Numerator != 1 || rational.Denominator != 2 {
		t.Errorf("Eval returned wrong value for (/ 2). Got %d/%d, want 1/2", rational.Numerator, rational.Denominator)
	}
}

// TestMultipleArgumentsOperations tests operations with multiple arguments
func TestMultipleArgumentsOperations(t *testing.T) {
	// Test (+ 1 2 3 4 5)
	addExpr := &ast.ListExpression{
		Token: tok.Token{Type: tok.LPAREN, Lexeme: "("},
		Elements: []ast.Expression{
			&ast.SymbolLiteral{Value: "+"},
			&ast.IntegerLiteral{Value: 1},
			&ast.IntegerLiteral{Value: 2},
			&ast.IntegerLiteral{Value: 3},
			&ast.IntegerLiteral{Value: 4},
			&ast.IntegerLiteral{Value: 5},
		},
	}

	// Test (* 2 3 4 5)
	mulExpr := &ast.ListExpression{
		Token: tok.Token{Type: tok.LPAREN, Lexeme: "("},
		Elements: []ast.Expression{
			&ast.SymbolLiteral{Value: "*"},
			&ast.IntegerLiteral{Value: 2},
			&ast.IntegerLiteral{Value: 3},
			&ast.IntegerLiteral{Value: 4},
			&ast.IntegerLiteral{Value: 5},
		},
	}

	// Create an environment and register built-in procedures
	env := obj.NewGlobalEnvironment()
	RegisterBuiltinProcedures(env)

	// Test addition
	addResult := Eval(addExpr, env)
	addInt, ok := addResult.(*obj.Integer)
	if !ok {
		t.Errorf("Eval didn't return Integer for addition. Got %T", addResult)
		return
	}

	if addInt.Value != 15 {
		t.Errorf("Eval returned wrong value for (+ 1 2 3 4 5). Got %d, want 15", addInt.Value)
	}

	// Test multiplication
	mulResult := Eval(mulExpr, env)
	mulInt, ok := mulResult.(*obj.Integer)
	if !ok {
		t.Errorf("Eval didn't return Integer for multiplication. Got %T", mulResult)
		return
	}

	if mulInt.Value != 120 {
		t.Errorf("Eval returned wrong value for (* 2 3 4 5). Got %d, want 120", mulInt.Value)
	}
}

// TestComparisonOperators tests the comparison operators <, >, <=, >=
func TestComparisonOperators1(t *testing.T) {
	// Create a global environment and register built-in procedures
	globalEnv := obj.NewGlobalEnvironment()
	RegisterBuiltinProcedures(globalEnv)
	env := globalEnv.Environment

	testCases := []struct {
		name           string
		expr           *ast.ListExpression
		expectedResult bool
		expectError    bool
	}{
		// Test < operator
		{
			name: "Integer less than",
			expr: &ast.ListExpression{
				Token: tok.Token{Type: tok.LPAREN, Lexeme: "("},
				Elements: []ast.Expression{
					&ast.SymbolLiteral{Value: "<"},
					&ast.IntegerLiteral{Value: 1},
					&ast.IntegerLiteral{Value: 2},
				},
			},
			expectedResult: true,
			expectError:    false,
		},
		{
			name: "Integer not less than",
			expr: &ast.ListExpression{
				Token: tok.Token{Type: tok.LPAREN, Lexeme: "("},
				Elements: []ast.Expression{
					&ast.SymbolLiteral{Value: "<"},
					&ast.IntegerLiteral{Value: 3},
					&ast.IntegerLiteral{Value: 2},
				},
			},
			expectedResult: false,
			expectError:    false,
		},
		{
			name: "Integer equal in less than",
			expr: &ast.ListExpression{
				Token: tok.Token{Type: tok.LPAREN, Lexeme: "("},
				Elements: []ast.Expression{
					&ast.SymbolLiteral{Value: "<"},
					&ast.IntegerLiteral{Value: 2},
					&ast.IntegerLiteral{Value: 2},
				},
			},
			expectedResult: false,
			expectError:    false,
		},

		// Test > operator
		{
			name: "Integer greater than",
			expr: &ast.ListExpression{
				Token: tok.Token{Type: tok.LPAREN, Lexeme: "("},
				Elements: []ast.Expression{
					&ast.SymbolLiteral{Value: ">"},
					&ast.IntegerLiteral{Value: 3},
					&ast.IntegerLiteral{Value: 2},
				},
			},
			expectedResult: true,
			expectError:    false,
		},
		{
			name: "Integer not greater than",
			expr: &ast.ListExpression{
				Token: tok.Token{Type: tok.LPAREN, Lexeme: "("},
				Elements: []ast.Expression{
					&ast.SymbolLiteral{Value: ">"},
					&ast.IntegerLiteral{Value: 1},
					&ast.IntegerLiteral{Value: 2},
				},
			},
			expectedResult: false,
			expectError:    false,
		},

		// Test <= operator
		{
			name: "Integer less than or equal",
			expr: &ast.ListExpression{
				Token: tok.Token{Type: tok.LPAREN, Lexeme: "("},
				Elements: []ast.Expression{
					&ast.SymbolLiteral{Value: "<="},
					&ast.IntegerLiteral{Value: 1},
					&ast.IntegerLiteral{Value: 2},
				},
			},
			expectedResult: true,
			expectError:    false,
		},
		{
			name: "Integer equal in less than or equal",
			expr: &ast.ListExpression{
				Token: tok.Token{Type: tok.LPAREN, Lexeme: "("},
				Elements: []ast.Expression{
					&ast.SymbolLiteral{Value: "<="},
					&ast.IntegerLiteral{Value: 2},
					&ast.IntegerLiteral{Value: 2},
				},
			},
			expectedResult: true,
			expectError:    false,
		},

		// Test >= operator
		{
			name: "Integer greater than or equal",
			expr: &ast.ListExpression{
				Token: tok.Token{Type: tok.LPAREN, Lexeme: "("},
				Elements: []ast.Expression{
					&ast.SymbolLiteral{Value: ">="},
					&ast.IntegerLiteral{Value: 2},
					&ast.IntegerLiteral{Value: 1},
				},
			},
			expectedResult: true,
			expectError:    false,
		},
		{
			name: "Integer equal in greater than or equal",
			expr: &ast.ListExpression{
				Token: tok.Token{Type: tok.LPAREN, Lexeme: "("},
				Elements: []ast.Expression{
					&ast.SymbolLiteral{Value: ">="},
					&ast.IntegerLiteral{Value: 2},
					&ast.IntegerLiteral{Value: 2},
				},
			},
			expectedResult: true,
			expectError:    false,
		},

		// Test mixed number types
		{
			name: "Mixed number types comparison",
			expr: &ast.ListExpression{
				Token: tok.Token{Type: tok.LPAREN, Lexeme: "("},
				Elements: []ast.Expression{
					&ast.SymbolLiteral{Value: ">"},
					&ast.RationalLiteral{Numerator: 3, Denominator: 2},
					&ast.IntegerLiteral{Value: 1},
				},
			},
			expectedResult: true,
			expectError:    false,
		},

		// Test error cases
		{
			name: "Not enough arguments",
			expr: &ast.ListExpression{
				Token: tok.Token{Type: tok.LPAREN, Lexeme: "("},
				Elements: []ast.Expression{
					&ast.SymbolLiteral{Value: "<"},
					&ast.IntegerLiteral{Value: 1},
				},
			},
			expectedResult: false,
			expectError:    true,
		},
		{
			name: "Non-number arguments",
			expr: &ast.ListExpression{
				Token: tok.Token{Type: tok.LPAREN, Lexeme: "("},
				Elements: []ast.Expression{
					&ast.SymbolLiteral{Value: "<"},
					&ast.SymbolLiteral{Value: "a"},
					&ast.SymbolLiteral{Value: "b"},
				},
			},
			expectedResult: false,
			expectError:    true,
		},
	}

	for _, tc := range testCases {
		t.Run(tc.name, func(t *testing.T) {
			result := Eval(tc.expr, env)

			if tc.expectError {
				if _, ok := result.(*obj.Error); !ok {
					t.Errorf("Expected error, got %T", result)
				}
				return
			}

			boolResult, ok := result.(*obj.Boolean)
			if !ok {
				t.Errorf("Expected Boolean result, got %T", result)
				return
			}

			if boolResult.Value != tc.expectedResult {
				t.Errorf("Expected %v, got %v", tc.expectedResult, boolResult.Value)
			}
		})
	}
}
